import sys
import os
import re
import send2trash
import subprocess
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QFileDialog, QLineEdit, QLabel, \
    QTableWidget, QTableWidgetItem, QHeaderView, QMessageBox, QHBoxLayout, QComboBox, QMenu,QAbstractItemView
from PyQt5.QtCore import Qt

class FileProcessor(QWidget):
    def __init__(self):
        super().__init__()

        self.folder_path = ""
        self.filtered_files = []  # 存储筛选后的文件
        self.current_page = 0  # 当前页
        self.page_size = 20  # 每页显示的文件数
        self.file_status = {}  # 存储文件状态的字典
        # 定义视频文件后缀列表
        self.video_extensions = ['.avi', '.mpg', '.mpeg', '.mov', '.MOV', '.wmv', '.WMV','.asf','.ASF', '.rm', '.RM','.mkv','.MKV', '.flv', '.FLV','.3gp','.3GP','.ram','.RAM',
                                 '.mp4','.MP4', '.wav', '.WAV']
        
        self.zs_all_file = []
        self.zs_success = []
        self.zs_failed = []


        self.initUI()

    def initUI(self):
        self.setWindowTitle("文件处理工具")
        self.resize(900, 600)

        layout = QVBoxLayout()

        # 选择文件夹
        folder_layout = QHBoxLayout()
        self.folder_label = QLabel("选择文件夹:")
        folder_layout.addWidget(self.folder_label)

        self.select_folder_btn = QPushButton("选择文件夹")
        self.select_folder_btn.clicked.connect(self.select_folder)
        folder_layout.addWidget(self.select_folder_btn)

        layout.addLayout(folder_layout)

        # 关键词搜索
        keyword_layout = QHBoxLayout()
        self.keyword_label = QLabel("搜索关键词:")
        keyword_layout.addWidget(self.keyword_label)

        self.keyword_input = QLineEdit()
        self.keyword_input.setPlaceholderText("输入搜索关键词")
        keyword_layout.addWidget(self.keyword_input)
        self.search_btn = QPushButton("搜索")
        self.search_btn.clicked.connect(self.load_files)
        keyword_layout.addWidget(self.search_btn)
        layout.addLayout(keyword_layout)

        # 状态筛选
        filter_layout = QHBoxLayout()
        self.filter_label = QLabel("筛选状态:")
        filter_layout.addWidget(self.filter_label)

        self.status_filter = QComboBox()
        self.status_filter.addItems(["全部", "处理成功", "处理失败"])
        self.status_filter.currentTextChanged.connect(self.load_files)
        filter_layout.addWidget(self.status_filter)

        layout.addLayout(filter_layout)

        # 刷新按钮
        refresh_layout = QHBoxLayout()
        self.file_count_label = QLabel("总文件数: 0")
        refresh_layout.addWidget(self.file_count_label)

        self.refresh_btn = QPushButton("刷新")
        self.refresh_btn.clicked.connect(self.load_files)
        refresh_layout.addWidget(self.refresh_btn)

        layout.addLayout(refresh_layout)

        # 文件表格
        self.file_table = QTableWidget()
        self.file_table.setColumnCount(3)
        self.file_table.setHorizontalHeaderLabels(["文件名", "原始路径", "当前状态"])
        self.file_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.file_table.setEditTriggers(QAbstractItemView.NoEditTriggers)
        # 启用右键菜单
        self.file_table.setContextMenuPolicy(Qt.CustomContextMenu)
        self.file_table.customContextMenuRequested.connect(self.show_context_menu)
        layout.addWidget(self.file_table)

        # 页码控制
        page_layout = QHBoxLayout()

        self.prev_page_btn = QPushButton("上一页")
        self.prev_page_btn.clicked.connect(self.prev_page)
        page_layout.addWidget(self.prev_page_btn)

        self.page_input = QLineEdit()
        self.page_input.setPlaceholderText("输入页码")
        self.page_input.setFixedWidth(60)
        page_layout.addWidget(self.page_input)

        self.jump_btn = QPushButton("跳转")
        self.jump_btn.clicked.connect(self.jump_to_page)
        page_layout.addWidget(self.jump_btn)

        self.next_page_btn = QPushButton("下一页")
        self.next_page_btn.clicked.connect(self.next_page)
        page_layout.addWidget(self.next_page_btn)

        self.page_label = QLabel("第 1 页 / 共 1 页")
        page_layout.addWidget(self.page_label)

        self.process_btn = QPushButton("处理文件")
        self.process_btn.clicked.connect(self.process_files)
        layout.addWidget(self.process_btn)

        layout.addLayout(page_layout)

        self.setLayout(layout)
        self.current_page = 0
        self.files_per_page = 50
        self.filtered_files = []

    def get_all_files(self, folder_path):
        """递归获取文件夹下所有文件（改为使用绝对路径）"""
        all_files = []
        try:
            for entry in os.scandir(folder_path):
                if entry.is_file():
                    # 直接将文件条目的路径转换为绝对路径
                    abs_path = os.path.abspath(entry.path)
                    all_files.append(abs_path)
                elif entry.is_dir():
                    # 递归遍历子文件夹
                    all_files.extend(self.get_all_files(entry.path))
        except PermissionError:
            QMessageBox.warning(self, "警告", f"无法访问文件夹: {folder_path}")
        except Exception as e:
            QMessageBox.warning(self, "错误", f"遍历文件夹时出错: {str(e)}")
        return all_files

    def jump_to_page(self):
        """跳转到指定页码"""
        try:
            page_number = int(self.page_input.text().strip()) - 1
            if 0 <= page_number < (len(self.filtered_files) // self.files_per_page) + 1:
                self.current_page = page_number
                self.update_table()
            else:
                QMessageBox.warning(self, "错误", "页码超出范围！")
        except ValueError:
            QMessageBox.warning(self, "错误", "请输入有效的页码！")

    def select_folder(self):
        folder = QFileDialog.getExistingDirectory(self, "选择文件夹")
        if folder:
            self.folder_label.setText(f"已选择: {folder}")
            self.folder_path = folder
            self.load_files()

    def load_files(self):
        """加载文件，并根据关键词和状态进行筛选"""
        if not hasattr(self, 'folder_path') or not self.folder_path or not os.path.exists(self.folder_path):
            QMessageBox.warning(self, "错误", "请先选择有效的文件夹！")
            return
        # 关键词
        keyword = self.keyword_input.text().strip()
        # 用户选择的状态，"全部"、"处理成功"、"处理失败"
        selected_status = self.status_filter.currentText()

        if selected_status in ["处理成功", "处理失败"]:
            keyword = ""
            files = []
        else:
            # 绝对路径
            files = self.get_all_files(self.folder_path)

        try:
            if self.file_status:
                for file_path, file_status in self.file_status.items():
                    files.append(file_path)

        except Exception as e:
            QMessageBox.warning(self, "错误", f"读取文件夹失败: {str(e)}")
            return

        filtered_files = set()
        basenamelist = set()

        for file_path in files:
            # 获取文件名和扩展名
            basename = os.path.basename(file_path)
            name, ext = os.path.splitext(basename) 

            # 获取文件当前状态
            current_status = self.file_status.get(file_path, "")

            # 根据状态筛选
            status_match = False
            if selected_status == "全部":
                status_match = True
            elif selected_status == "处理成功":
                status_match = any(success_text in current_status for success_text in
                                   ["成功：已移动到回收站", "成功：已重命名", "成功：重命名结果"])
            elif selected_status == "处理失败":
                status_match = any(fail_text in current_status for fail_text in
                                   ["失败：文件不存在", "失败：移动到回收站失败", "失败：文件被占用", "失败：重命名时出错"])

            # 根据关键词筛选
            keyword_match = True
            if keyword and not current_status:
                pattern = re.escape(keyword)
                keyword_match = bool(re.search(f"({pattern})", name))

            if status_match and keyword_match:
                filtered_files.add(file_path)

        self.filtered_files = list(filtered_files)
        total_found = len(self.filtered_files)

        # 更新 UI
        self.file_count_label.setText(f"总文件数: {total_found}")
        self.current_page = 0  # 回到第一页
        self.update_table()
        # 强制刷新界面
        QApplication.processEvents()

    def update_file_status(self, file_path, status):
        """更新文件的处理状态"""
        self.file_status[file_path] = status

        # 在当前页中查找文件并更新状态
        for row in range(self.file_table.rowCount()):
            table_file_path = self.file_table.item(row, 1).text()
            if table_file_path == file_path:
                status_item = QTableWidgetItem(status)
                if "失败" in status:
                    status_item.setForeground(Qt.red)  # 失败状态显示红色
                elif "成功" in status:
                    status_item.setForeground(Qt.green)  # 成功状态显示绿色
                self.file_table.setItem(row, 2, status_item)
                # 强制更新表格显示
                self.file_table.viewport().update()
                break

    def update_table(self):
        """更新表格显示当前页数据，并调整列宽自适应"""
        # 清空当前表格
        self.file_table.clearContents()

        total_files = len(self.filtered_files)
        total_pages = (total_files // self.files_per_page) + (1 if total_files % self.files_per_page else 0)

        start_index = self.current_page * self.files_per_page
        end_index = start_index + self.files_per_page
        page_files = self.filtered_files[start_index:end_index]

        self.file_table.setRowCount(len(page_files))

        for row, file_path in enumerate(page_files):
            basename = os.path.basename(file_path)

            # 获取文件当前状态，如果没有状态则设为""
            current_status = self.file_status.get(file_path, "")

            # 添加文件信息及状态
            name_item = QTableWidgetItem(basename)
            path_item = QTableWidgetItem(file_path)
            status_item = QTableWidgetItem(current_status)

            # 设置状态颜色
            if "失败" in current_status:
                status_item.setForeground(Qt.red)
            elif "成功" in current_status:
                status_item.setForeground(Qt.green)

            self.file_table.setItem(row, 0, name_item)
            self.file_table.setItem(row, 1, path_item)
            self.file_table.setItem(row, 2, status_item)

        # 更新页码标签
        self.page_label.setText(f"第 {self.current_page + 1} 页 / 共 {max(1, total_pages)} 页")

        # 自适应列宽
        self.file_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
        self.file_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
        self.file_table.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeToContents)

        # 强制刷新界面
        self.file_table.viewport().update()
        QApplication.processEvents()

    def prev_page(self):
        """上一页"""
        if self.current_page > 0:
            self.current_page -= 1
            self.update_table()

    def next_page(self):
        """下一页"""
        total_pages = (len(self.filtered_files) + self.page_size - 1) // self.page_size
        if self.current_page < total_pages - 1:
            self.current_page += 1
            self.update_table()

    def process_files(self):
        """处理当前表格中的文件（基于筛选后的文件列表）"""
        if not self.folder_path:
            QMessageBox.warning(self, "错误", "请先选择文件夹！")
            return

        if not self.filtered_files:
            QMessageBox.warning(self, "错误", "没有符合条件的文件可处理！")
            return

        keyword = self.keyword_input.text().strip()
        if not keyword:
            QMessageBox.warning(self, "错误", "关键词为空")
            return

        # 禁用按钮，防止重复操作
        self.select_folder_btn.setEnabled(False)
        self.keyword_input.setEnabled(False)
        self.status_filter.setEnabled(False)
        self.refresh_btn.setEnabled(False)
        self.process_btn.setEnabled(False)

        # 获取当前文件夹下所有文件
        cur_all_files = self.get_all_files(self.folder_path)

        files_processed = 0
        try:
            # 第一步：找到所有精确匹配关键词的文件
            keyword_files = [] # key是文件路径，value是修改后的文件名
            pattern = re.escape(keyword)  # 转义特殊字符
            for file_path in self.filtered_files:
                # 分离出目录、文件名以及扩展名
                dir_path = os.path.dirname(file_path)            # 不带文件名的目录路径
                # 先提取「文件名」，再分离出「扩展名」
                filename = os.path.basename(file_path)
                name, ext = os.path.splitext(filename)
                if ext not in self.video_extensions:
                    # 仅处理视频格式文件
                    continue
                if re.search(f"({pattern})", name):
                    # 获取不含关键词的基本名称
                    base_name = name.replace(keyword, "").strip()
                    # 拼接改后带后缀的完整文件名
                    after_full_filename = f"{base_name}{ext}"
                    # 构造 改后路径(含后缀)
                    after_path = os.path.join(os.path.dirname(file_path), after_full_filename)
                    # 构造 改后路径(不含后缀)
                    after_path_no_ext = os.path.join(os.path.dirname(file_path), base_name)

                    # 将信息存到字典
                    file_info = {
                        "before_path": file_path,        # 改前路径(含后缀)
                        "after_path": after_path,        # 改后路径(含后缀)
                        "after_path_no_ext": after_path_no_ext,        # 改后路径(不含后缀)
                        "before_filename": name,         # 改前文件名(不带后缀)
                        "after_filename": base_name,     # 改后文件名(不带后缀)
                        "dir_path": dir_path,            # 不带文件名的目录路径
                        "ext": ext                       # 文件后缀(含'.')
                    }
                    keyword_files.append(file_info)

            # 第二步：先收集所有需要删除的文件
            all_files_to_delete = [] # 用于收集所有需要删除的文件
            visited_paths = set()  # 用于避免重复收集同一路径
            processed_files = set()  # 用于跟踪所有处理过的文件

            # 判断改后会出现重名的文件
            for file_info in keyword_files:
                # 从 file_info 中拿到目录与改后文件名(不含后缀)
                dir_path = file_info["dir_path"]
                after_filename = file_info["after_filename"]

                # 依次组合出 candidate = dir_path/after_filename.后缀
                # 逐个视频扩展名来构造 candidate
                for ext in self.video_extensions:
                    candidate_name = f"{after_filename}{ext}"
                    candidate_path = os.path.join(dir_path, candidate_name)
                    candidate_path = os.path.normpath(candidate_path)

                    # 如果文件存在且尚未收集
                    if candidate_path in cur_all_files and candidate_path not in visited_paths:
                        visited_paths.add(candidate_path)
                        video_file_info = {
                            "after_path": candidate_path,        # 绝对路径(含后缀)
                            "dir_path": dir_path,          # 文件所在目录
                            "ext": ext,              # 后缀(含点)
                        }
                        all_files_to_delete.append(video_file_info)          

            # 第三步：删除收集到的文件
            for video_file_info in all_files_to_delete:
                try:
                    if os.path.isfile(video_file_info["after_path"]):
                        send2trash.send2trash(video_file_info["after_path"])
                        self.file_status[video_file_info["after_path"]] = "成功：已移动到回收站"
                        processed_files.add(video_file_info["after_path"])
                        self.update_file_status(video_file_info["after_path"], "成功：已移动到回收站")
                        files_processed += 1
                        QApplication.processEvents()
                except Exception as e:
                    error_msg = f"失败：移动到回收站失败 - {str(e)}"
                    self.file_status[video_file_info["after_path"]] = error_msg
                    processed_files.add(video_file_info["after_path"])
                    self.update_file_status(video_file_info["after_path"], error_msg)
                    # 根据当前筛选状态立即重新加载文件
                    # self.load_files()
                    QApplication.processEvents()

            # 第四步：重命名源文件
            for file_info in keyword_files:
                try:
                    if not os.path.isfile(file_info["before_path"]):
                        error_msg = "失败：文件不存在"
                        self.file_status[file_info["before_path"]] = error_msg
                        processed_files.add(file_info["before_path"])
                        self.update_file_status(file_info["before_path"], error_msg)
                        QApplication.processEvents()
                        continue

                    try:
                        # 重命名文件
                        os.rename(file_info["before_path"], file_info["after_path"])
                        self.file_status[file_info["before_path"]] = "成功：已重命名"
                        self.file_status[file_info["after_path"]] = "成功：重命名结果"
                        processed_files.add(file_info["before_path"])
                        processed_files.add(file_info["after_path"])
                        self.update_file_status(file_info["before_path"], "成功：已重命名")
                        # self.update_file_status(new_filename, "成功：重命名结果")
                        # 根据当前筛选状态立即重新加载文件
                        # self.load_files()
                        QApplication.processEvents()
                        files_processed += 1

                    except PermissionError:
                        error_msg = "失败：文件被占用或无权限访问"
                        self.file_status[file_info["before_path"]] = error_msg
                        processed_files.add(file_info["before_path"])
                        self.update_file_status(file_info["before_path"], error_msg)
                        # 根据当前筛选状态立即重新加载文件
                        self.load_files()
                        QApplication.processEvents()
                    except OSError as e:
                        error_msg = f"失败：重命名时出错 - {str(e)}"
                        self.file_status[file_info["before_path"]] = error_msg
                        processed_files.add(file_info["before_path"])
                        self.update_file_status(file_info["before_path"], error_msg)
                        # 根据当前筛选状态立即重新加载文件
                        self.load_files()
                        QApplication.processEvents()

                except Exception as e:
                    error_msg = f"失败：{str(e)}"
                    self.file_status[file_info["before_path"]] = error_msg
                    processed_files.add(file_info["before_path"])
                    self.update_file_status(file_info["before_path"], error_msg)
                    # 根据当前筛选状态立即重新加载文件
                    self.load_files()
                    QApplication.processEvents()

            # 更新所有未处理文件的状态为""
            for file_path in self.get_all_files(self.folder_path):
                if file_path not in processed_files:
                    self.file_status[file_path] = ""
                    self.update_file_status(file_path, "")

        except Exception as e:
            QMessageBox.warning(self, "错误", f"处理文件时出错: {str(e)}")

        # 恢复按钮
        self.select_folder_btn.setEnabled(True)
        self.keyword_input.setEnabled(True)
        self.status_filter.setEnabled(True)
        self.refresh_btn.setEnabled(True)
        self.process_btn.setEnabled(True)

        # 显示处理结果
        if files_processed > 0:
            QMessageBox.information(self, "完成", f"已处理 {files_processed} 个文件。")
        else:
            QMessageBox.information(self, "完成", "没有文件需要处理。")

    def show_context_menu(self, pos):
        """显示右键菜单"""
        # 获取当前选中的行
        row = self.file_table.rowAt(pos.y())
        if row >= 0:  # 确保点击在有效行上
            menu = QMenu(self)

            # # 添加"打开文件所在文件夹"选项
            # open_folder_action = menu.addAction("打开文件所在文件夹")
            # # 添加"打开文件"选项
            # open_file_action = menu.addAction("打开文件")

             # 合并成一个“打开所在位置”
            open_location_action = menu.addAction("打开所在位置")

            # 显示菜单
            action = menu.exec_(self.file_table.viewport().mapToGlobal(pos))

            if action:
                # 获取文件完整路径
                file_path = self.file_table.item(row, 1).text()

                if action == open_location_action:
                    self.open_containing_folder(file_path)

    def open_containing_folder(self, file_path):
        """打开文件所在文件夹（如果文件已删除，则打开所在目录）"""
        folder_path = os.path.dirname(file_path)
        try:
            # 如果文件依然存在，则执行“选中该文件”的操作
            if os.path.exists(file_path):
                if os.name == 'nt':
                    # Windows：使用 subprocess.Popen + CREATE_NO_WINDOW，避免弹出黑框
                    # /select, 后面注意不要加空格
                    cmd = ["explorer", "/select," + file_path]
                    subprocess.Popen(cmd, creationflags=subprocess.CREATE_NO_WINDOW)
                elif os.name == 'darwin':
                    os.system(f'open -R \"{file_path}\"')
                else:
                    os.system(f'xdg-open \"{folder_path}\"')
            else:
                # 文件不存在，则仅打开所在文件夹
                if os.path.exists(folder_path):
                    if os.name == 'nt':
                        cmd = ["explorer", folder_path]
                        subprocess.Popen(cmd, creationflags=subprocess.CREATE_NO_WINDOW)
                    elif os.name == 'darwin':
                        os.system(f'open \"{folder_path}\"')
                    else:
                        os.system(f'xdg-open \"{folder_path}\"')
                else:
                    # 所在文件夹不存在，弹出提示
                    QMessageBox.warning(self, "提示", "文件已被删除，且所在文件夹也不存在。")
        except Exception as e:
            QMessageBox.warning(self, "错误", f"无法打开文件夹: {str(e)}")

    def open_file(self, file_path):
        """打开文件"""
        try:
            # 对于Windows系统
            if os.name == 'nt':
                os.startfile(file_path)
            # 对于macOS系统
            elif os.name == 'darwin':
                os.system(f'open "{file_path}"')
            # 对于Linux系统
            else:
                os.system(f'xdg-open "{file_path}"')
        except Exception as e:
            QMessageBox.warning(self, "错误", f"无法打开文件: {str(e)}")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = FileProcessor()
    window.show()
    sys.exit(app.exec_())
